import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.*;

/**
 * Applet principale: contiene un JPanel con all'interno i 5 pulsanti del menu'
 * e un altro JPanel che puo contenere JTabello o JBlocchi in base all'attivita' scelta dall'utente.
 * 
 * JTabella permette di visualizzare una tabella con un numero di colonne variabile
 * nel caso specifico e' una tabella triangolare. Questi valori vengono presi dalla classe 
 * DifferenzeFinite che si occupa di far procedere l'algoritmo alle differenze finite.
 * 
 * JBlocchi permette di impostare una lista di etichette che l'utente ha il compito di riordinare
 * 
 * Ogni volta che viene premuto uno dei bottoni viene lanciata ActionPerformed()
 * Siccome e' necessario premere piu volte lo stesso pulsante esiste un intero "step"
 * che viene usato come contatore, per sapere a che punto dell'esercitazione e' arrivato l'utente.
 */
public class AppletPresentazione extends JApplet implements ActionListener{

    JTabella jt; // JTabella che conterra' la tabella triangolare per le differenze finite.
    /**
     * 5 Pulsanti del menu
     */
    JButton Demo1 ; 
    JButton Demo2 ;
    JButton Demo3 ;
    JButton Demo4 ;
    JButton Demo5 ;
    JScrollPane jsp; //Pannello con scorrimento che conterra' JTabella perche non ha una dimension fissa, e spesso supera i 500px
    JBlocchi  jd; //inizializzato se l'utente decide di riordinare l'algoritmo
    int step; //contatore avanzamento utente
    JPanel p; //contiene i 5 pulsanti Demo
    JPanel intro; //contiene il logo iniziale
    Container cnt; //punta a this.getContentPane()

    DifferenzeFinite interpolante; //classe dell'algoritmo alle differenze finite

    public void init() {

        cnt = getContentPane(); //prendo il contentiore su cui lavorare, richiesto dalla guida di JApplet

       step=-1; 

        cnt.setPreferredSize(new Dimension(400,300));
        
        cnt.setLayout(new BorderLayout());

        p = new JPanel();
        p.setLayout(new BoxLayout(p,BoxLayout.Y_AXIS));
        Demo1 = new JButton();
        Demo1.setText("Dimostrazione dell'algoritmo a differenze finite");

        Demo1.addActionListener(this);
        Demo2 = new JButton();
        Demo2.addActionListener(this);
        Demo2.setText("Prova l'algoritmo a differenze finite su dati arbitrari");
        Demo3 = new JButton();
        Demo3.addActionListener(this);
        Demo3.setText("Inserisci manualmente i dati calcolati dall'algoritmo");
        Demo4 = new JButton();
        Demo4.addActionListener(this);
        Demo4.setText("Se l'algoritmo e' concluso, Premi qui per valutare il polinomio");
        Demo5 = new JButton();
        Demo5.addActionListener(this);
        Demo5.setText("Ricostruisci il diagramma a blocchi dell'algoritmo");

        // Su tutti i pulsanti ho impostato come listener delle azioni, questa classe che implementa i metodi di ActionListener
        
        p.add(Demo1);
        p.add(Demo2);
        p.add(Demo3);
        p.add(Demo4);
        p.add(Demo5);

        cnt.add(p, "North");

        
         // Inserisco il logo inziale.
        intro = new JPanel();
        intro.setLayout(new GridLayout(2,1));
        JLabel jl0 = new JLabel ("Differenze Finite");
        JLabel jl1 = new JLabel ("Valeria Ragagnin");
        jl0.setFont(new java.awt.Font("Dialog", 1, 36)); 
        jl1.setHorizontalAlignment(JLabel.LEFT);
        intro.add(jl0);
        intro.add(jl1);

        cnt.add(intro,"Center");
      
    }

    // Discruggo e dealloco i componenti
    public void destroy()
    {
        super.destroy();
        cnt.removeAll();
        cnt.validate();

        p.removeAll();
        p.validate();

        if(jsp!=null)
        {
            jsp.removeAll();
            jsp.validate();
        }

        Demo1=null;
        Demo2=null;
        Demo2=null;
        Demo3=null;
        Demo4=null;
        Demo5=null;
        jsp=null;
        jd=null;
        p=null;
        intro=null;
        interpolante=null;
        Demo1=null;
        Demo2=null;
        Demo2=null;
        Demo3=null;
        Demo4=null;
        Demo5=null;
        jsp=null;
        jd=null;
        p=null;
        intro=null;
        interpolante=null;
    }

    // viene chiamato alla premuta del pulsante
    public void actionPerformed(ActionEvent e) {
        //se step = -1, ovvero lo stesso valore impostato su init() il logo e' ancora presente e viene riosso
        //in piu viene portato il contatore step a zero
        if(step==-1)
        {
            cnt.remove(intro);
            cnt.validate();
            step++;
        }
        
        //cerco il pulsante che ha generato l'evento e lancio la relativa funzione di dimostrazione
        
        Object o  = e.getSource();
        if(o==Demo1)
        {
            demo1();
        }  else if(o==Demo3)
        {
            demo3();
        }else if(o==Demo2)
        {
            demo2();
        }
        else if(o==Demo4)
        {

            demo4();
        }
        else if(o==Demo5)
        {
            demo5();
        }
    }

    
    //Inizializza JTabella in base al numero n di ascisse inserite (o da inserire)
    //lancia un ECCEZIONE java.lang.NegativeArraySizeException se il chiamante
    //vuole inserire un nuero negativo di righe.
    public void initJTabella(int n) throws java.lang.NegativeArraySizeException
    {
        if(n<=0) throw new java.lang.NegativeArraySizeException();
        jt = new JTabella(n);
        
        jsp = new JScrollPane(jt); //inserisco JTabella in un pannello a scorrimento

        jsp.setVisible(true);

        cnt.add(jsp);

    }

    //polinomio d'esempio usato nella dimostrazione 1
    public static double p(double x)
    {
        return x*x*x+x+5.0;
    }

    //chiamata se viene esseguita la dimostrazione 1: visualizzazione guidata del procedere dell'algoritmo con dei numeri prefissati
    public void demo1()
    {

        if(step==0)
        {
            //disattivo gli altri pulsanti che non hanno senso di essere attivi durante un altra dimostrazione
            Demo2.setEnabled(false);
            Demo3.setEnabled(false);
            Demo5.setEnabled(false);

            //inizializza una lista per le ascisse e una per le ordinate
            ArrayList <Double> x = new ArrayList <Double> ();
            ArrayList <Double> fx = new ArrayList <Double> ();
            x.add(1.0);
            fx.add(p(1.0));
            x.add(2.0);
            fx.add(p(2.0));
            x.add(5.0);
            fx.add(p(5.0));
            x.add(2.5);
            fx.add(p(2.5));
            x.add(1.5);
            fx.add(p(1.5));

            //inizializzo DifferenzeFinte e JTabella con questi dati
            interpolante = new DifferenzeFinite(x,fx);
            initJTabella(5);
            jt.setData(interpolante.getData());
            jt.setEnabled(false);

            //cambio descrizione del pulsante
            Demo1.setText("Premi ancora per fare avanzare l'algoritmo");
            
            //aumento di un passo il procedimento della dimostrazione
            step++;
        }else if(step==1)            //questo blocco viene eseguito quando il pulsante e' stato premuto piu di una volta
        {

            //step() fa avanzare l'algoritmo ritorna true se l'algoritmo e' finito

            if(interpolante.step())
            {
                step=5;
                Demo1.setText("Algoritmo terminato");
                Demo1.setEnabled(false);
            }

            //prelevo i dati usati dall'algoritmo sotto forma di arraylist di arraylist di double
            //e li passo a Jtabella che aggiorni le JTextBox
            
            jt.setData(interpolante.getData());

        }
    }

 
    //chiamata se viene esseguita la dimostrazione 3: l'utente inserisce sia i dati iniziali da interpolare che quelli ricavati passo passo dall'algoritmo
    public void demo3()
    {
        if(step==0)
        {

            int n=0;


            String s = JOptionPane.showInputDialog("Inserisci il numero di punti da interpolare");
            try{
                n = Integer.parseInt(s);
                initJTabella(n);
            } catch (java.lang.NegativeArraySizeException ex) {

                JOptionPane.showMessageDialog(this, "Errore: Inserire un numero positivo");
                return;
            } catch (NumberFormatException ex) {
                JOptionPane.showMessageDialog(this, "Errore: Inserire un numero");
                return;

            }

          //disattivo gli altri pulsanti che non hanno senso di essere attivi durante un altra dimostrazione
            Demo2.setEnabled(false);
            Demo5.setEnabled(false);
            Demo1.setEnabled(false);

            Demo3.setText("Premi ancora dopo aver inserito i punti");
            step++;

        }
        
        
        else if(step==1) 
        {
            //prelevo i dati inseriti su JTabella dall'utente e li inserisco dentro DifferenzeFinite
            ArrayList <Double> x = jt.getData().get(0);
            ArrayList <Double> fx = jt.getData().get(1);

            interpolante = new DifferenzeFinite(x,fx);

            //interpolante.print();
            //jt.setData(interpolante.getData());
            Demo3.setText("Premi di nuovo dopo aver scritto i dati richiesti dall'algorimo.");
            step++;//avanzo solo se l'ultima volta l'utente ha inserito i dati correttamente.

            interpolante.step();//avvio il primo passo dell'algoritmo
           
        }
        else if(step==2)
        {
            

             boolean algoritmoFinito=interpolante.isAlgorithmFinished();
            
            //prelevo i dati generati da DifferenzeFinite
            //e idati presenti in JTabella inseriti dall'utente
            //li confrotero'
            ArrayList <ArrayList <Double>> algoritmo = interpolante.getData();
            ArrayList <ArrayList <Double>> utente = jt.getData();

            
            String errore = "";
            boolean sbaglioUtente = false; //flag segna se l'utente ha commesso un errore,
            //quindi se i dati inseriti differiscono da quelli forniti da DifferenzeFinite
            
            //confronto le due liste di liste di double e segnalo l'eventuale differenza all'utente
            //se sono diverse imposto il flag sbaglioUtente = true, cosicche al prossimo click l'algoritmo non proceda
            //ma controlli che i dati fino a questo step siano OK
            for(int i  = 0;i<algoritmo.size();i++)
                for(int j  = 0;j<algoritmo.get(i).size();j++)
                {
                      if(!algoritmo.get(i).get(j).equals(utente.get(i).get(j)))
                    { 
                        //aumento di 1 riga e colonna perche l'umano medio comincia a contare da 1
                        errore = "colonna "+(i+1)+", riga "+(j+1)+".\nvalore dell'algoritmo:"+algoritmo.get(i).get(j)+"\nvalorei inserito:"+utente.get(i).get(j) ;
                        sbaglioUtente = true;
                    }
            }

            if(sbaglioUtente)
            {
                JOptionPane.showMessageDialog(this, "Dati inseriti sbagliati: \n"+errore);
            }
            
            //se l'utente non ha sbaglio ed ha eseguito l'ultimo passaggio, l'algoritmo e' terminato.
            if(!sbaglioUtente && algoritmoFinito)
            {
                Demo3.setText("I dati inseriti sono corretti e l'algoritmo terminato.");
                Demo3.setEnabled(false);
            }

            //avanzo solo se l'ultima volta l'utente ha inserito i dati correttamente.
            if(!sbaglioUtente)
               interpolante.step();
           

        }

    }

    
    //dimostrazione 2: l'utente inserisce dati arbitrari e controlla il procedere dell'algoritmo
    //dopo la prima parte in cui chiede i dati all'utente la funzione e' uguale a demo1()
    public void demo2()
    {
        if(step==0)
        {
            int n=0;
            String s = JOptionPane.showInputDialog("Inserisci il numero di punti da interpolare");
            try
            {
                n = Integer.parseInt(s);
                initJTabella(n);
            } catch (java.lang.NegativeArraySizeException ex) {

                JOptionPane.showMessageDialog(this, "Errore: Inserire un numero positivo");
                return;
            } catch (NumberFormatException ex) {
                JOptionPane.showMessageDialog(this, "Errore: Inserire un numero");
                return;
            } 

         //disattivo gli altri pulsanti che non hanno senso di essere attivi durante un altra dimostrazione
             Demo1.setEnabled(false);
            Demo3.setEnabled(false);
            Demo5.setEnabled(false);

            jt.setEnabled(false);
            Demo2.setText("Premi ancora dopo aver inserito i punti");
            step++;

        }
        else if(step==1)
        {
            ArrayList <Double> x = jt.getData().get(0);
            ArrayList <Double> fx = jt.getData().get(1);

            interpolante = new DifferenzeFinite(x,fx);

            Demo2.setText("Premi ancora per far avanzare l'algoritmo");
            step++;
        }
        else if(step==2)
        {
            if(interpolante.step())
            {
                step=5;
                Demo2.setText("Algoritmo terminato.");
                Demo2.setEnabled(false);
            }
            
            jt.setData(interpolante.getData());

        }

    }

    //dimostrazione 4: interpola il polinomio in un determinato punto richiesto dall'utente
    //cattura le eccezioni lanciate da interpolante nel caso si tenti di interpolarlo quando l' algoritmo non e' concluso
    public void demo4()
    {

        try {
            String pol = interpolante.polinomio();

            String s = JOptionPane.showInputDialog("f(x)="+pol+"\nInserisci x");
            double x = Double.parseDouble(s);
            JOptionPane.showMessageDialog(this, "f(x)="+interpolante.p(x));

        } catch (AlgorithmNotFinishedException ex) {
            JOptionPane.showMessageDialog(this, "Errore: L'algoritmo non e' ancora stato completato!");

        } catch (NumberFormatException ex) {
            JOptionPane.showMessageDialog(this, "Errore: Inserire un numero");

        } catch (NullPointerException ex) {
            JOptionPane.showMessageDialog(this, "Errore: Non e' stato inserito alcun punto per interpolare il polinomio");
        }

    }

    //dimostrazione 4: L'utente riordina i blocchi dell'algoritmo descritto a parole
    public void demo5()
    {
        if(step==0)
        {

            //inizializzo la lista di sstringhe con i vari blocchi
            ArrayList <String> blocchi = new ArrayList <String> ();

            blocchi.add("Crea una tabella di N righe e 1 colonna.");
            blocchi.add("Leggi N punti e inserisci le ordinate nella tabella.");
           blocchi.add("Prendi  gli elementi dell'ultima colonna creata e  per ogni elemento di questa colonna fino al penultimo");
             blocchi.add("calcola la differenza tra un elemento e il successivo e dividila per la differenza delle ascisse che hanno portato a tale differenza.");
            blocchi.add("Alla tabella aggiungi una colonna il cui numero di righe e' quello delle righe della colonna precedente meno uno.");
            blocchi.add("In questa colonna inserisci i rapporti incrementali appena calcolati");
            blocchi.add("Se i numeri inseriti NON sono tutti uguali ritorna al blocco '2'.");
            blocchi.add("Leggi un valore, detto x, su cui interpolare il polinomio.");
            blocchi.add("Per ogni i intero compreso tra 0 e n-1");
          
            blocchi.add("per le prime i-1 ascisse inserite nel blocco 1 dell'algoritmo, calcolarne la differenza con x e moltiplicare tra loro queste differenze");
            blocchi.add("moltiplica questo per il valore la prima riga dell'i-esima colonna");//
            blocchi.add("La somma di questi n contributi e' il valore del polinomio interpolato nel punto x.");

            //la passo al costruttore di JBloccchi che, per ogni stringa, crea una classe JBlocco che contenga una JLabel con quel valore
            jd =  new JBlocchi (blocchi);
            
            Demo1.setEnabled(false);
            Demo2.setEnabled(false);
            Demo3.setEnabled(false);
            Demo4.setEnabled(false);
            
            Demo5.setText("Premi qui per verificare se l'ordine dei blocchi e' giusto");
            jsp = new JScrollPane(jd);
            jsp.setVisible(true);

            cnt.add(jsp);
            step++;
        }else if(step==1)
        {
            if(jd.indiciCoincidono())
                JOptionPane.showMessageDialog(this, "L'algoritmo inserito e' CORRETTO");
            else
                JOptionPane.showMessageDialog(this, "Mi dipiace l'ordine degli elementi e' SBAGLIATO");
        }
    }

}
